home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr11 / pd0836.zip / MAIN.C < prev    next >
C/C++ Source or Header  |  1993-04-30  |  66KB  |  2,020 lines

  1. /***    MAIN.C - DSDUMP/DSSNAP main program
  2.  *
  3.  *      DSDUMP: Display DoubleSpace Compressed Volume File (CVF) information.
  4.  *      DSSNAP: Copy CVF system area to a file.
  5.  *
  6.  *      Version 1.00.58  12-Mar-1993
  7.  *
  8.  *      Notes:
  9.  *          -DSNAP => builds DSSNAP program;
  10.  *                    if not defined, builds DSDUMP program.
  11.  */
  12.  
  13. #include <ctype.h>
  14. #include <dos.h>    // get thin DOS INT 21h call interface
  15. #include <fcntl.h>
  16. #include <io.h>
  17. #include <memory.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21.  
  22. #include "cvf.h"        // Get CVF format
  23. #include "drvinfo.h"    // IsDoubleSpaceDrive()
  24.  
  25.  
  26. //*****************************************************************************
  27. //* CONSTANTS                                                                 *
  28. //*****************************************************************************
  29.  
  30. #define szProduct "DoubleSpace" // product name
  31.  
  32. #define verMAJOR         0      // Major version number (N.xxx)
  33. #define verMINOR        58      // Minor version number (x.NN)
  34.  
  35.  
  36. #define cbPERLINE       16      // Number of hex bytes per output line
  37. #define cCOLUMNS         2      // Number of columns of hex output per line
  38.  
  39. #define cbFILEBUFFER    (cbPERLINE*128) // Size of file buffer
  40.  
  41. #define cbOUTPUTLINEMAX 100     // Maximum line length on stdout
  42.  
  43. #define FR_CURRENT_POS  -1      // FileRead iseek value to read from current
  44.                                 //  position in the file.
  45.  
  46. #define chNOTPRINTABLE  '.'     // Used for non-printable characters in HEX out
  47.  
  48. #define chSWITCH        '/'     // Command line switch character
  49. #define chSWITCH2       '-'     // Alternate Command line switch character
  50.  
  51. #define chRANGE_START_END   '-' // Start,End range separator (/M2-37)
  52. #define chRANGE_START_COUNT '+' // Start,Length range separator (/C20+3)
  53.  
  54. #define cbDIR_ENT    32    // Number bytes per DOS directory entry
  55.  
  56. #define szCVF_ROOT  "DBLSPACE"  // Base name of CVF file
  57.  
  58. #define seqBAD          255     // Invalid CVF sequence number
  59. #define seqMAX          254     // Largest valid CVF sequence number
  60.  
  61. #define cchMAXFILEPATH  129     // Maximum length of a file path
  62.  
  63.  
  64. //*****************************************************************************
  65. //* TYPES                                                                     *
  66. //*****************************************************************************
  67.  
  68. typedef int          FILEHANDLE; /* fh */  // File handle
  69. typedef int          RETCODE;    /* rc */  // Return code
  70. typedef int          SEQ;        /* seq */ // CVF sequence number
  71. typedef unsigned int UINT;       /* ui */  // unsigned int
  72.  
  73.  
  74. /***    RANGE - Range of a regions specified on command line
  75.  *
  76.  *      dwRANGE_BAD - value for iFirst or iLast that indicates an error
  77.  *      dwRANGE_DEFAULT - value for iFirst or iLast that indicates no
  78.  *                          command line value was supplied.
  79.  */
  80. typedef struct RANGE_t { /* range */
  81.     DWORD   iFirst;                     // First entry to display
  82.     DWORD   iLast;                      // Last entry to display
  83. } RANGE;
  84. typedef RANGE *PRANGE;  /* prange */
  85. #define dwRANGE_BAD     0xFFFFFFFF      // Bad range value
  86. #define dwRANGE_DEFAULT 0xFFFFFFFE      // Default range value
  87.  
  88.  
  89. /***    REGION - file region characterization
  90.  *
  91.  *      Used to record data on the BitFAT, MDFAT, boot sector, DOS FAT,
  92.  *      DOS root directory, and the sector heap.
  93.  *
  94.  *      The cbTotal is the reserved amount of space in the file for the
  95.  *      region.  This is usually a limiting factor on growing the CVF.
  96.  *
  97.  *      The cbActive is the length of the region (starting from the
  98.  *      front) that is valid for the CVF at its current size.
  99.  */
  100. typedef struct {    /* reg */
  101.     long    ibStart;        // Byte offset in file of region start
  102.     long    cbTotal;        // Total length of region in bytes
  103.     long    cbActive;       // Active area of region, in bytes
  104.     BOOL    fDisplay;       // TRUE => region is interesting
  105.     char   *pszName;        // Name of region
  106. } REGION;
  107. typedef REGION *PREGION; /* preg */
  108.  
  109.  
  110. /***    INDEXREGION - index into areg of file regions
  111.  *
  112.  *      WARNING: This enumeration must be kept parallel to areg!
  113.  */
  114. typedef enum { /* ireg */
  115.     iregMDBPB,
  116.     iregBITFAT,
  117.     iregRESERVED1,
  118.     iregMDFAT,
  119.     iregRESERVED2,
  120.     iregDOSBOOT,
  121.     iregRESERVED3,
  122.     iregDOSFAT,
  123.     iregDOSROOTDIR,
  124.     iregRESERVED4,
  125.     iregSECTORHEAP,
  126.     cregMAX,                        // Count of ireg's
  127. } INDEXREGION;
  128.  
  129.  
  130. /***    MEMBERTYPE - Display type for a structure member
  131.  *
  132.  *      Used to describe the display format of a structure member.
  133.  *
  134.  *      WARNING: The elements of this enumeration must match the array
  135.  *               cbFromMT!
  136.  */
  137. typedef enum { /* mt */
  138.     mtBYTE,     // printf("%02x",(WORD)BYTE);
  139.     mtBYTE3,    // printf("%02x %02x %02x",(WORD)ab[0],(WORD)ab[1],(WORD)ab[2]);
  140.     mtCHAR,     // printf("%c",char);
  141.     mtCHAR8,    // printf("%8s",ach[8]);
  142.     mtDWORD,    // printf("%08lx",DWORD);
  143.     mtINT1,     // printf("%3d",(short)char);
  144.     mtINT2,     // printf("%5d",short);
  145.     mtINT4,     // printf("%10ld",long);
  146.     mtUINT1,    // printf("%3u",(WORD)BYTE);
  147.     mtUINT2,    // printf("%5u",WORD);
  148.     mtUINT4,    // printf("%10lu",DWORD);
  149.     mtWORD,     // printf("%04x",WORD);
  150. } MEMBERTYPE;
  151. #define mtHIDE  0x80    // OR with other mtXXXX => do not display
  152.  
  153.  
  154. /***    MEMBERINFO - Describes a stucture member for formatting purposes
  155.  *
  156.  */
  157. typedef struct {    /* mi */
  158.     MEMBERTYPE  mt;
  159.     char       *psz;    // description
  160. } MEMBERINFO;
  161. typedef MEMBERINFO *PMEMBERINFO; /* pmi */
  162.  
  163.  
  164. /***    GLOBAL - structure for global variables
  165.  *
  166.  *      This is to make them obvious in the sources
  167.  *
  168.  */
  169. typedef struct {
  170.     int     argc;                   // argc parameter passed to main(...)
  171.     char  **argv;                   // argv parameter passed to main(...)
  172.     long    cbCVF;                  // Size of CVF in bytes
  173.     long    ibCVFStamp2;            // Position of 2nd MD_STAMP in CVF
  174.     char    chDrive;                // Drive letter of mounted CVF, else 0
  175.     MDBPB   mp;             // MDBPB
  176.     BOOL    fIgnoreSigCheck;        // TRUE => Ignore signature check
  177. #ifdef    SNAP
  178. #else
  179.     RANGE   rangeBitFAT;            // Display range for BitFAT
  180.     RANGE   rangeBitFATValid;       // Valid range for BitFAT
  181.     RANGE   rangeCVF;               // Display range for CVF sectors
  182.     RANGE   rangeCVFValid;          // Valid range for CVF sectors
  183.     RANGE   rangeMDFAT;             // Display range for MDFAT
  184.     RANGE   rangeMDFATValid;        // Valid MDFAT range
  185.     RANGE   rangeHeap;              // Display range for Sector Heap
  186.     RANGE   rangeHeapValid;         // Valid range for Sector Heap
  187.     BOOL    fShowAddresses;         // TRUE => -a specified on command line
  188.     BOOL    fShowBitFAT;            // TRUE => -b specified on command line
  189.     BOOL    fShowCVFSectors;        // TRUE => -c specified on command line
  190.     BOOL    fShowDOSBoot;           // TRUE => -t specified on command line
  191.     BOOL    fShowDOSFAT;            // TRUE => -f specified on command line
  192.     BOOL    fShowDOSRootDir;        // TRUE => -r specified on command line
  193.     BOOL    fShowFragmentation;     // TRUE => -g specified on command line
  194.     BOOL    fShowHeader;            // TRUE => -h specified on command line
  195.     BOOL    fShowHeap;              // TRUE => -s specified on command line
  196.     BOOL    fShowMDFAT;             // TRUE => -m specified on command line
  197.     BOOL    fShowVerbose;           // TRUE => -v specified on command line
  198.     BYTE    ab[cbFILEBUFFER];       // File buffer
  199. #endif
  200.     char    ach[cbOUTPUTLINEMAX];   // Line output buffer
  201.     char    achCVFName[cchMAXFILEPATH]; // CVF file name
  202. #ifdef    SNAP
  203.     char    achOutFileName[cchMAXFILEPATH]; // snap output file name
  204. #endif
  205. } GLOBAL;
  206.  
  207.  
  208. //*****************************************************************************
  209. //* VARIABLES                                                                 *
  210. //*****************************************************************************
  211.  
  212. /***    g - Global variables
  213.  *
  214.  */
  215. GLOBAL  g;
  216.  
  217.  
  218. #ifndef SNAP    // -----------------------------------------------------
  219.  
  220. /***    cbFromMt - Get size of structure member from MEMBERTYPE
  221.  *
  222.  *      WARNING: The elements of this array must exactly parallel the
  223.  *               MEMBERTYPE enumeration!
  224.  */
  225. int cbFromMt[] = {
  226.     1,  // mtBYTE
  227.     3,  // mtBYTE3
  228.     1,  // mtCHAR
  229.     8,  // mtCHAR8
  230.     4,  // mtDWORD
  231.     1,  // mtINT1
  232.     2,  // mtINT2
  233.     4,  // mtINT4
  234.     1,  // mtUINT1
  235.     2,  // mtUINT2
  236.     4,  // mtUINT4
  237.     2,  // mtWORD
  238. };
  239.  
  240.  
  241. /***    amiMDBPB - Description of MDBPB structure members
  242.  *
  243.  */
  244. MEMBERINFO amiMDBPB[] = {
  245.   { mtBYTE3       , "jmpBOOT         : Jump to bootstrap routine"            },
  246.   { mtCHAR8       , "achOEMName[8]   : OEM Name"                             },
  247.   { mtUINT2       , "cbPerSec        : Count of bytes per sector"            },
  248.   { mtUINT1       , "csecPerClu      : Count of sectors per cluster"         },
  249.   { mtUINT2       , "csecReserved    : Count of reserved sectors"            },
  250.   { mtUINT1       , "cFATs           : Count of FATs"                        },
  251.   { mtUINT2       , "cRootDirEntries : Count of root directory entries"      },
  252.   { mtUINT2       , "csecTotalWORD   : Count of total sectors"               },
  253.   { mtBYTE        , "bMedia          : Media byte"                           },
  254.   { mtUINT2       , "csecFAT         : Count of sectors occupied by the FAT" },
  255.   { mtUINT2       , "csecPerTrack    : Count of sectors per track"           },
  256.   { mtUINT2       , "cHeads          : Count of heads"                       },
  257.   { mtUINT4       , "csecHidden      : Count of hidden sectors"              },
  258.   { mtUINT4       , "csecTotalDWORD  : Count of total sectors"               },
  259.   { mtUINT2       , "secMDFATStart   : Logical sector of MDFAT"              },
  260.   { mtUINT1       , "nLog2cbPerSec   : Log base 2 of cbPerSec"               },
  261.   { mtUINT2       , "csecMDReserved  : Number of sectors reserved by MD"     },
  262.   { mtUINT2       , "secRootDirStart : Logical sector of root directory"     },
  263.   { mtUINT2       , "secHeapStart    : Logical sector of sector heap"        },
  264.   { mtUINT2       , "cluFirstData    : DOS/DBLSPACE cluster offset"          },
  265.   { mtUINT1       , "cpageBitFAT     : Count of 'pages' in the BitFAT"       },
  266.   { mtWORD |mtHIDE, "RESERVED1       : Reserved1"                            },
  267.   { mtUINT1       , "nLog2csecPerClu : Log base 2 of csecPerClu"             },
  268.   { mtWORD |mtHIDE, "RESERVED2       : Reserved2"                            },
  269.   { mtUINT4|mtHIDE, "RESERVED3       : Reserved3"                            },
  270.   { mtUINT4|mtHIDE, "RESERVED4       : Reserved4"                            },
  271.   { mtBYTE        , "f12BitFAT       : 1 => 12-bit FAT, 0 => 16-bit FAT"     },
  272.   { mtUINT2       , "cmbCVFMax       : Maximum CVF size, in megabytes"       },
  273. };
  274.  
  275. #endif    // ifndef SNAP -------------------------------------------------
  276.  
  277.  
  278. /***    areg - CVF regions
  279.  *
  280.  *      This array is filled in by reading the MDBPB and performing
  281.  *      computations with its members.
  282.  *
  283.  *      WARNING: This array must be kept parallel to INDEXREGION!
  284.  */
  285. REGION areg[] = {
  286.     /* iStart, cbTotal, cbActive, fInteresting, pszName */
  287.     /*                1234567890123456                  */
  288.     { 0L, 0L, 0L, 1, "MDBPB"          },
  289.     { 0L, 0L, 0L, 1, "BitFAT"         },
  290.     { 0L, 0L, 0L, 0, "<reserved1>"    },
  291.     { 0L, 0L, 0L, 1, "MDFAT"          },
  292.     { 0L, 0L, 0L, 0, "<reserved2>"    },
  293.     { 0L, 0L, 0L, 1, "Boot Sector"    },
  294.     { 0L, 0L, 0L, 0, "<reserved3>"    },
  295.     { 0L, 0L, 0L, 1, "DOS FAT"        },
  296.     { 0L, 0L, 0L, 1, "Root Directory" },
  297.     { 0L, 0L, 0L, 0, "<reserved4>"    },
  298.     { 0L, 0L, 0L, 1, "Sector Heap"    },
  299. };
  300.  
  301.  
  302. /***    szFromMDFAT - Map MDFAT flag bits to strings for display
  303.  *
  304.  */
  305. char *szFromMDFAT[] = {
  306.     "F,C",       // free,      compressed
  307.     "F,U",       // free,      uncompressed
  308.     "A,C",       // allocated, compressed
  309.     "A,U"        // allocated, uncompressed
  310. };
  311.  
  312. /***    apszSyntax - Syntax help
  313.  *
  314.  */
  315.  
  316. #define isynSUMMARY 2   // Index of syntax summary line
  317.  
  318. char *apszSyntax[] = {
  319. #ifdef SNAP
  320.     "Takes a snapshot of the system area of a DoubleSpace drive.",
  321.     "",
  322.     "DSSNAP [/I] [drive] [/O=file]",  // Must be isynSUMMARY'nd line
  323.     "",
  324.     "  drive   DoubleSpace drive, default is current drive;",
  325.     "            An explicit CVF name may also be specified.",
  326.     "  /I      Ignore DoubleSpace signature check",
  327.     "  /O=file File name of snapshot file; default is SNAPSHOT.DS",
  328. #else // SNAP
  329.     "Displays formatted DoubleSpace drive information.",
  330.     "",
  331.     "DSDUMP [/AFHIRTV] [/BCMS[range]] [drive]", // Must be isynSUMMARY'nd line
  332.     "",
  333.     "  drive DoubleSpace drive or CVF; default is current drive.",
  334.     "  /A    Display Addresses of file regions",
  335.     "  /F    Display DOS FAT",
  336.     "  /G    Display BitFAT fragmentation report",
  337.     "  /H    Display header",
  338.     "  /I    Ignore DoubleSpace signature check",
  339.     "  /R    Display DOS root directory",
  340.     "  /T    Display DOS boot sector",
  341.     "  /V    Display everything (verbose)",
  342.     "  /B    Display BitFAT",
  343.     "  /C    Display CVF sectors",
  344.     "  /M    Display MDFAT",
  345.     "  /S    Display Sector Heap",
  346.     "  range An optional range of entries to display.  Use 'n' to select entry n,",
  347.     "        'n-m' to select entries n through m, and 'n+c' to select c entries",
  348.     "        starting at entry n.  If omitted, all entries are displayed.",
  349.     "        Examples: /M27, /S137-142, /C34+4",
  350. #endif    // SNAP
  351. };
  352.  
  353. #ifdef    SNAP
  354.  
  355. char szOutName[] = "SNAPSHOT.DS";       // default out file name
  356.  
  357. #endif
  358.  
  359. //*****************************************************************************
  360. //* FUNCTION PROTOTYPES                                                       *
  361. //*****************************************************************************
  362.  
  363.  
  364. #ifdef    SNAP
  365.  
  366. void      SnapCVF(FILEHANDLE fh);
  367.  
  368. #else
  369.  
  370. void      CheckRange(PRANGE prangeA, PRANGE prangeB, char *psz);
  371. void      DumpBitFAT(FILEHANDLE fh);
  372. void      DumpCVFSectors(FILEHANDLE fh);
  373. void      DumpDOSBoot(FILEHANDLE fh);
  374. void      DumpDOSFAT(FILEHANDLE fh);
  375. void      DumpDOSRootDir(FILEHANDLE fh);
  376. void      DumpMDBPB(void);
  377. void      DumpMDFAT(FILEHANDLE fh);
  378. void      DumpHex(FILEHANDLE fh, long iseek, long cb);
  379. void      DumpHexLine(long iseek, BYTE *pb);
  380. void      DumpHeap(FILEHANDLE fh);
  381. void      DumpSummary(BOOL fFull);
  382. void      FormatMember(char *pch,void *pv, MEMBERTYPE mt);
  383. char *    FormatPercent(char *psz,DWORD num,DWORD den);
  384. BOOL      GetBitFATBit(FILEHANDLE fh, DWORD i);
  385. void      PrintHeader(char *psz);
  386. void      PrintHeader2(char *psz1, char *psz2);
  387.  
  388. #endif    // ifndef SNAP
  389.  
  390. BOOL      BuildCVFName(char *psz, char ach[], int cb, char *chDrive);
  391. void      CheckSignature(FILEHANDLE fh, long seekpos, char *pszName);
  392. void      ComputeRegions(FILEHANDLE fh);
  393. void      Error(char *pszMsg, char *pszParm);
  394. RETCODE   FileRead(FILEHANDLE fh, long iseek, void *pb, WORD cb);
  395. BOOL      IsDriveSpec(char *psz);
  396. int cdecl main(int argc, char **argv);
  397. void      ParseArgs(int argc, char **argv);
  398. DWORD     ParseDecimal(char **ppch);
  399. void      ParseRange(PRANGE prange, char **ppch);
  400. void      PrintBanner(void);
  401. void      ShowSyntax(BOOL fFull);
  402.  
  403.  
  404. //*****************************************************************************
  405. //* FUNCTIONS                                                                 *
  406. //*****************************************************************************
  407.  
  408. /***    main - entry point
  409.  *
  410.  */
  411. int cdecl main(int argc, char **argv)
  412. {
  413.     FILEHANDLE  fh;
  414.     RETCODE     rc;
  415.  
  416.     // Announce ourselves
  417.     PrintBanner();
  418.  
  419. #ifdef SNAP
  420.     strcpy(g.achOutFileName,szOutName); // Set default output file name
  421. #endif
  422.  
  423.     // Get arguments
  424.     ParseArgs(argc,argv);               // If error, it exits
  425.  
  426.     // If CVF is mounted, make sure it is fully up-to-date
  427.     if (g.chDrive)
  428.         FlushDrive(g.chDrive - 'A', FDOP_DISK_RESET);
  429.  
  430.     // Open CVF
  431.     rc = _dos_open(g.achCVFName,O_RDONLY,&fh);
  432.     if (rc)
  433.         Error("Could not open %s",g.achCVFName);
  434.  
  435.     // Read MDBPB from file
  436.  
  437.     if (FileRead(fh,0L,&g.mp,sizeof(g.mp))) {
  438.         Error("Could not read MDBPB",NULL);
  439.     }
  440.  
  441.     // Compute CVF Regions
  442.  
  443.     ComputeRegions(fh);
  444.  
  445. #ifdef SNAP
  446.  
  447.     SnapCVF(fh);
  448.  
  449. #else    // SNAP
  450.  
  451.     // Check ranges for validity, if specified
  452.  
  453.     if (g.fShowBitFAT || g.fShowFragmentation)
  454.         CheckRange(&g.rangeBitFAT,&g.rangeBitFATValid,"BitFAT bit");
  455.  
  456.     if (g.fShowCVFSectors)
  457.         CheckRange(&g.rangeCVF,&g.rangeCVFValid,"CVF sector");
  458.  
  459.     if (g.fShowHeap)
  460.         CheckRange(&g.rangeHeap,&g.rangeHeapValid,"Heap sector");
  461.  
  462.     if (g.fShowMDFAT)
  463.         CheckRange(&g.rangeMDFAT,&g.rangeMDFATValid,"MDFAT entry");
  464.  
  465.     // Do operations indicated by flags
  466.  
  467.     DumpSummary(g.fShowAddresses);
  468.  
  469.     if (g.fShowHeader)
  470.         DumpMDBPB();
  471.  
  472.     if (g.fShowBitFAT || g.fShowFragmentation)
  473.         DumpBitFAT(fh);
  474.  
  475.     if (g.fShowCVFSectors)
  476.         DumpCVFSectors(fh);
  477.  
  478.     if (g.fShowMDFAT)
  479.         DumpMDFAT(fh);
  480.  
  481.     if (g.fShowDOSBoot)
  482.         DumpDOSBoot(fh);
  483.  
  484.     if (g.fShowDOSFAT)
  485.         DumpDOSFAT(fh);
  486.  
  487.     if (g.fShowDOSRootDir)
  488.     DumpDOSRootDir(fh);
  489.  
  490.     if (g.fShowHeap)
  491.         DumpHeap(fh);
  492.  
  493. #endif  // ifdef SNAP
  494.  
  495.     // Close CVF
  496.  
  497.     _dos_close(fh);
  498.  
  499.     // Done
  500.     return 0;
  501. }
  502.  
  503.  
  504. #ifndef SNAP    // ------------------------------------------------------
  505.  
  506.  
  507. /***    CheckRange - Check that one range is contained in another
  508.  *
  509.  *      Check range for inclusion; set default values, if indicated.
  510.  *
  511.  *      Entry
  512.  *          prangeA - range to be checked for inclusion
  513.  *          prangeB - range that should include rangeA
  514.  *          psz     - String for error message
  515.  *
  516.  *      Exit-Success
  517.  *          prangeA is included in prangeB;
  518.  *          *prangeA is updated to replace any dwRANGE_DEFAULT values
  519.  *          with the corresponding values from prangeB.
  520.  *
  521.  *      Exit-Failure
  522.  *          prangeA is not completely inside prangeB.
  523.  *          Error message printed, and program exits.
  524.  */
  525. void CheckRange(PRANGE prangeA, PRANGE prangeB, char *psz)
  526. {
  527.     char    ach[cbOUTPUTLINEMAX];
  528.  
  529.     // Change default values to the bounding range
  530.  
  531.     if (prangeA->iFirst == dwRANGE_DEFAULT)
  532.         prangeA->iFirst = prangeB->iFirst;
  533.  
  534.     if (prangeA->iLast == dwRANGE_DEFAULT)
  535.         prangeA->iLast = prangeB->iLast;
  536.  
  537.     // Check for inclusion
  538.  
  539.     if (prangeA->iFirst < prangeB->iFirst) {
  540.         sprintf(ach,"%s %ld is not in valid range %ld..%ld",
  541.             psz,prangeA->iFirst,prangeB->iFirst,prangeB->iLast);
  542.         Error(ach,"");
  543.     }
  544.  
  545.     if (prangeA->iLast > prangeB->iLast) {
  546.         sprintf(ach,"%s %ld is not in valid range %ld..%ld",
  547.             psz,prangeA->iLast,prangeB->iFirst,prangeB->iLast);
  548.         Error(ach,"");
  549.     }
  550. }
  551.  
  552.  
  553. /***    DumpSummary - Display summary report
  554.  *
  555.  *      Entry
  556.  *          fFull - TRUE if regions addresses should be displayed.
  557.  *          g.mp - Filled in with MD BPB from CVF already.
  558.  *
  559.  *      Exit
  560.  *          Summary written to stdout
  561.  */
  562. void DumpSummary(BOOL fFull)
  563. {
  564.     int     ireg;
  565.     long    cbSec;      // Count of bytes per sector
  566.  
  567.     printf("\n");
  568.     if (g.chDrive != 0)
  569.         printf("Drive: %c (mounted from %s)\n",g.chDrive,g.achCVFName);
  570.     else
  571.         printf("File: %s\n",g.achCVFName);
  572.  
  573.     // Are we just printing the header?
  574.     if (!fFull)     // Yes
  575.         return;
  576.  
  577.     cbSec = g.mp.cbPerSec;
  578.     /*        12345678901234567890123456789012345678901234567890123456789012345678901234567890  */
  579.     printf("Area            Start:     Sector  Length:  cSectors  Active:  cSectors\n");
  580.     printf("--------------  -----------------  -----------------  -----------------\n");
  581.  
  582.     for (ireg=0; ireg<cregMAX; ireg++) {
  583.         printf("%-14s  %9ld %7ld  %9ld %7ld  %9ld %7ld\n",
  584.                 areg[ireg].pszName,
  585.                 areg[ireg].ibStart, areg[ireg].ibStart/cbSec,
  586.                 areg[ireg].cbTotal, areg[ireg].cbTotal/cbSec,
  587.                 areg[ireg].cbActive, (areg[ireg].cbActive+cbSec-1)/cbSec);
  588.     // NOTE: Make sure sector count of active area includes last sector
  589.     }
  590. }
  591.  
  592.  
  593. /***    DumpMDBPB - Dump the MDBPB
  594.  *
  595.  *      Entry
  596.  *          g.mp - Filled in with MDBPB from CVF already
  597.  *
  598.  *      Exit
  599.  *          Formatted MDBPB written to stdout
  600.  */
  601. void DumpMDBPB(void)
  602. {
  603.     int     i;
  604.     void   *pv;
  605.  
  606.     PrintHeader("MDBPB Structure");
  607.  
  608.     // Print out each member of MDBPB structure
  609.  
  610.     pv = &g.mp;
  611.     for (i=0; i<(sizeof(amiMDBPB)/sizeof(MEMBERINFO)); i++) {
  612.         if (!(amiMDBPB[i].mt & mtHIDE)) {   // Not a hidden member
  613.             FormatMember(g.ach,pv,amiMDBPB[i].mt); // Create displayable value
  614.             printf("%10s = %s\n",g.ach,amiMDBPB[i].psz); // Display it
  615.         }
  616.         (char *)pv += cbFromMt[amiMDBPB[i].mt];  // Next structure member
  617.     }
  618. }
  619.  
  620.  
  621. /***    DumpMDFAT - Dump the MDFAT
  622.  *
  623.  *      Note that MDFAT entries do not correspond directly with DOS FAT
  624.  *      entries. DBLSPACE.BIN calculates it's MDFAT 'cluster' number by
  625.  *      dividing the sector number passed from MS-DOS by the sectors per
  626.  *      cluster.  For example, with 8k clusters, FAT cluster 2 (the first
  627.  *      cluster available for data) might start at sector 192.
  628.  *      192 / 16 (sectors per cluster) = 12, the MDFAT entry number.  A DOS
  629.  *      FAT entry number can be converted to it's corresponding MDFAT entry
  630.  *      number by adding the cluFirstData value to the DOS cluster number.
  631.  *      For the above example, DOS cluster 2 + cluFirstData (10) = MDFAT
  632.  *      entry 12.  The cluFirstData field contains the number of MDFAT
  633.  *      entries ('clusters') occupied by the DOS boot record, reserved area,
  634.  *      and root directory.
  635.  *
  636.  *      Entry
  637.  *          fh           - file handle of CVF
  638.  *          g.mp         - filled in the MDBPB from CVF
  639.  *          g.rangeMDFAT - range of entries to display
  640.  *
  641.  *      Exit
  642.  *          Formatted MDFAT written to stdout
  643.  */
  644. void DumpMDFAT(FILEHANDLE fh)
  645. {
  646.     char        ach[cbOUTPUTLINEMAX];
  647.     UINT        clPiece;
  648.     DWORD       cur_mdfat;
  649.     DWORD      *pMDFAT;
  650.     UINT        iLine;
  651.     DWORD       mdfat_entry;
  652.  
  653.     sprintf(ach,"MDFAT entries %ld to %ld",
  654.                     g.rangeMDFAT.iFirst,g.rangeMDFAT.iLast);
  655.     PrintHeader2(ach,
  656.                   "Flags: A=Allocated, F=Free, C=Compressed, U=Uncompressed");
  657.  
  658.     /*      1234567890123456789012345678901234567890 */
  659.     printf("FAT#  MDFAT# Flags cUnc cCmp secStart\n");
  660.     printf("----- ------ ----- ---- ---- --------\n");
  661.  
  662.     cur_mdfat = g.rangeMDFAT.iFirst;
  663.  
  664.     // Process one piece at a time (limited by our buffer size)
  665.     while (cur_mdfat <= g.rangeMDFAT.iLast) {
  666.  
  667.         // Count of MDFAT entries to process
  668.         clPiece = (WORD)min(g.rangeMDFAT.iLast-cur_mdfat+1,
  669.                             sizeof(g.ab)/cbMDFATENTRY);
  670.  
  671.         // Get a piece of the file to print
  672.         if (FileRead(fh,
  673.                      areg[iregMDFAT].ibStart + cbMDFATENTRY*cur_mdfat,
  674.                      g.ab,
  675.                      sizeof(g.ab))) {
  676.             Error("Read of CVF failed!",NULL);
  677.         }
  678.  
  679.         // Display the piece one line at a time
  680.         pMDFAT = (DWORD *)g.ab;
  681.         for (iLine=0; iLine<clPiece; iLine++) {
  682.             mdfat_entry = pMDFAT[iLine]; // fetch the MDFAT entry
  683.             printf("%5ld %6ld %5s  %2d   %2d  %8ld\n",
  684.                      cur_mdfat - g.rangeMDFATValid.iFirst + 2, // FAT number
  685.                      cur_mdfat,                             // MDFAT number
  686.                      szFromMDFAT[3 & (mdfat_entry >> 30)],  // Flags
  687.                      1+ (int) (15 & (mdfat_entry >> 26)),   // Count uncomp
  688.                      1+ (int) (15 & (mdfat_entry >> 22)),   // Count comp
  689.                      mdfat_entry & 0x3fffff);               // Sector #
  690.             cur_mdfat++;
  691.         }
  692.     }
  693.  
  694. }
  695.  
  696.  
  697. /***    DumpBitFAT - Dump the BitFAT and/or fragmentation report
  698.  *
  699.  *      Entry
  700.  *          fh            - file handle of CVF
  701.  *          g.mp          - filled in the MDBPB from CVF
  702.  *          g.rangeBitFAT - range of BitFAT entries to display
  703.  *          g.fShowBitFAT - TRUE => show BitFAT entries
  704.  *          g.fShowFragmentation - TRUE => show fragmentation report
  705.  *
  706.  *      Exit
  707.  *          Formatted BitFAT and/or fragmentation report written to stdout
  708.  */
  709. void DumpBitFAT(FILEHANDLE fh)
  710. {
  711.  
  712. #define iFREE   0       // Index for counts of free sectors
  713. #define iUSED   1       // Index for counts of used sectors
  714.  
  715.     DWORD   aavgRun16[2];           // Average length of runs >= 16 sectors
  716.     char    ach[cbOUTPUTLINEMAX];   // Line output buffer
  717.     char    ach1[10];               // Buffer for formatting percentages
  718.     char    ach2[10];               // Buffer for formatting percentages
  719.     DWORD   acRun[16][2];           // Count of runs[length][free/used]
  720.     DWORD   asumRun[2];             // Sum of all sectors [free/used]
  721.     DWORD   asumRun16[2];           // Sum of sectors in runs >= 16 sec [f/u]
  722.     DWORD   csecRun;
  723.     BOOL    fBit;
  724.     DWORD   i;
  725.     int     j;
  726.     DWORD   iRunFirst;
  727.     DWORD   iRunLast;
  728.  
  729.     if (g.fShowBitFAT) {                // Print header for BitFAT
  730.         sprintf(ach,"BitFAT for heap sectors %ld to %ld",
  731.                         g.rangeBitFAT.iFirst,g.rangeBitFAT.iLast);
  732.         PrintHeader(ach);
  733.     }
  734.  
  735.     if (g.fShowFragmentation) {         // Zero counters
  736.         for (j=0; j<16; j++) {
  737.             acRun[j][iFREE] = 0;
  738.             acRun[j][iUSED] = 0;
  739.         }
  740.         asumRun16[iFREE] = 0;
  741.         asumRun16[iUSED] = 0;
  742.     }
  743.  
  744.     i = g.rangeBitFAT.iFirst;
  745.     iRunFirst = i;
  746.     fBit = GetBitFATBit(fh,i);
  747.  
  748.     i++;
  749.     while (i <= g.rangeBitFAT.iLast) {
  750.         // Find run of bits with same setting
  751.         while ( (i <= g.rangeBitFAT.iLast) && (fBit == GetBitFATBit(fh,i)) ) {
  752.             i++;
  753.         }
  754.         // Note run, prepare to look for end of next run
  755.         iRunLast = i-1;
  756.         csecRun = iRunLast - iRunFirst + 1;
  757.         if (g.fShowBitFAT) {
  758.             printf("%s %ld to %ld, length %ld\n", fBit ? "USED" : "free",
  759.                        iRunFirst, iRunLast, csecRun);
  760.         }
  761.         if (g.fShowFragmentation) {     // Accumulate statistics
  762.             if (csecRun < 16) {         // Run less than 16 sectors
  763.                 acRun[csecRun-1][fBit] += 1;
  764.             }
  765.             else {                      // Run >= 16 sectors
  766.                 acRun[15][fBit] += 1;   // Count run
  767.                 asumRun16[fBit] += csecRun; // Sum number of sectors
  768.             }
  769.         }
  770.         iRunFirst = i;  // New first bit
  771.         fBit = !fBit;   // Bit is reversed
  772.     }
  773.  
  774.     if (g.fShowFragmentation) {         // Generate report
  775.         PrintHeader("Fragmentation Report");
  776.         printf("\n");
  777.         printf("       free   used\n");
  778.         printf("size   count  count  free %%   used %%\n");
  779.         printf("-----  -----  -----  -------  -------\n");
  780.  
  781.         // Compute average length of runs >= 16 sectors long
  782.         for (j=0; j<2; j++) {
  783.             if (acRun[15][j] > 0)
  784.                 aavgRun16[j] = asumRun16[j]/acRun[15][j];
  785.             else
  786.                 aavgRun16[j] = 0;
  787.             asumRun[j] = asumRun16[j];  // Sum of all runs, by type
  788.         }
  789.  
  790.         // Sum up 1..15 sector runs
  791.         for (j=0; j<15; j++) {
  792.             asumRun[iFREE] += acRun[j][iFREE]*(j+1);
  793.             asumRun[iUSED] += acRun[j][iUSED]*(j+1);
  794.         }
  795.  
  796.         // Print 1..15 sector runs stats
  797.         for (j=0; j<15; j++) {
  798.             printf("%5d  %5ld  %5ld  %7s  %7s\n",
  799.                     j+1, acRun[j][iFREE], acRun[j][iUSED],
  800.                     FormatPercent(ach1,(j+1)*acRun[j][iFREE],asumRun[iFREE]),
  801.                     FormatPercent(ach2,(j+1)*acRun[j][iUSED],asumRun[iUSED])
  802.                   );
  803.         }
  804.  
  805.         // Print 16+ sector run stats
  806.         printf("%5ld  %5ld         %7s           Free runs >= 16 sectors\n",
  807.                aavgRun16[iFREE], acRun[15][iFREE],
  808.                FormatPercent(ach1,
  809.                            aavgRun16[iFREE]*acRun[15][iFREE],asumRun[iFREE])
  810.               );
  811.         printf("%5ld         %5ld           %7s  Used runs >= 16 sectors\n",
  812.                aavgRun16[iUSED], acRun[15][iUSED],
  813.                FormatPercent(ach2,
  814.                            aavgRun16[iUSED]*acRun[15][iUSED],asumRun[iUSED])
  815.               );
  816.         printf("---------------------\n");
  817.         printf("Total Free: %9ld\n", asumRun[iFREE]);
  818.         printf("Total Used: %9ld\n", asumRun[iUSED]);
  819.         printf("---------------------\n");
  820.         printf("TOTAL       %9ld\n", asumRun[iFREE]+asumRun[iUSED]);
  821.     }
  822. }
  823.  
  824.  
  825. /***    FormatPercentage - Make PSZ from percentage of two dwords
  826.  *
  827.  *      Entry
  828.  *          psz - Buffer to receive formatted percentage
  829.  *          num - Numerator
  830.  *          den - Denominator
  831.  *
  832.  *      Exit
  833.  *          psz has percentage num/den, e.g., " 67.03%"
  834.  *          returns psz, for use by caller
  835.  *
  836.  *      NOTE: If den is zero, then result is "  0.00%".
  837.  */
  838. char *FormatPercent(char *psz,DWORD num,DWORD den)
  839. {
  840.     DWORD   dwUnit=0;           // nnn.00 portion
  841.     DWORD   dwFrac=0;           // 000.nn portion
  842.  
  843.     if (den != 0) {
  844.         // NOTE: Num is at most 1024*1024, as that is the maximum number
  845.         //       of sectors in a CVF sector heap.  Since a DWORD holds
  846.         //       numbers up to 4 billion, we won't overflow here.
  847.  
  848.         dwUnit = (100*num)/den;
  849.  
  850.         // NOTE: To get the .00% amount, we have to reduce the magnitude
  851.         //       of num (100*100*num would overflow 4 billion in the worst
  852.         //       case).  So we subtract the units amount before we do the
  853.         //       second multiply by 100.
  854.  
  855.         dwFrac = (100*(num*100 - dwUnit*den))/den;
  856.     }
  857.  
  858.     // Now format result
  859.  
  860.     sprintf(psz,"%3ld.%02ld%%",dwUnit,dwFrac);
  861.  
  862.     return psz;
  863. }
  864.  
  865.  
  866. /***    GetBitFATBit - Get one bit from the BitFAT
  867.  *
  868.  *      Entry
  869.  *          fh   - file handle of CVF
  870.  *          i    - index of bit to return (using Sector Heap numbering!)
  871.  *          g.mp - filled in the MDBPB from CVF
  872.  *
  873.  *      Exit
  874.  *          Returns value of BitFAT[i]
  875.  *
  876.  *      NOTE: g.ab is used as a BitFAT "page" cache (in this case, we
  877.  *            do no use the standard 2K BitFAT page size), so the caller
  878.  *            must ensure that g.ab is not modified between calls to
  879.  *            this routine!
  880.  */
  881. BOOL GetBitFATBit(FILEHANDLE fh, DWORD i)
  882. {
  883.     long        ib;                     // Byte index into BitFAT
  884.     int         ibit;                   // Bit index into BitFAT word
  885.     int         iw;                     // Word index into BitFAT page
  886.     int         ipage;                  // Page index into BitFAT
  887.     static int  ipageCached=-1;         // Index of cached BitFAT page in g.ab
  888.     WORD       *pw=(WORD *)g.ab;        // Pointer to BitFAT page
  889.     RETCODE     rc;
  890.  
  891.     // Adjust index to be zero based
  892.     i -= g.rangeBitFATValid.iFirst;
  893.  
  894.     // Get byte index of BitFAT word containing the requested bit
  895.     ib = (i>>4)<<1;
  896.  
  897.     // Make sure we have BitFAT page in cache
  898.     ipage = (int)(ib / sizeof(g.ab));
  899.     if (ipage != ipageCached) {         // Read page of bitfat
  900.         rc = FileRead(fh,
  901.                       areg[iregBITFAT].ibStart + ipage*sizeof(g.ab),
  902.                       g.ab,
  903.                       sizeof(g.ab)
  904.                      );
  905.         if (rc)
  906.             Error("Read failed on CVF BitFAT",NULL);
  907.         ipageCached = ipage;            // New cached page
  908.     }
  909.  
  910.     // Now extract the requested bit
  911.     iw = (int)(ib - ipageCached*(long)sizeof(g.ab)) / 2;
  912.     ibit = 15 - ((WORD)i % 16);         // 0th bit is at bit 15!
  913. //BUG  printf("iw=%d, pw[iw]=%04x, ibit=%2d\n",iw,pw[iw],ibit);
  914.     if (ibit == 0)
  915.         return pw[iw] & 0x01;           // Skip the shift
  916.     else
  917.         return (pw[iw] >> ibit) & 0x01; // Shift and mask
  918. }
  919.  
  920.  
  921. /***    DumpCVFSectors - Dump raw sectors from CVF
  922.  *
  923.  *      Entry
  924.  *          fh   - file handle of CVF
  925.  *          g.mp - filled in the MDBPB from CVF
  926.  *          g.rangeCVF - range of CVF sectors to dump
  927.  *
  928.  *      Exit
  929.  *         Selected range from CVF displayed
  930.  */
  931. void DumpCVFSectors(FILEHANDLE fh)
  932. {
  933.     char    ach[cbOUTPUTLINEMAX];
  934.     DWORD   iSec;
  935.     DWORD   offStart;
  936.  
  937.     sprintf(ach,"CVF Sectors %ld to %ld",
  938.                     g.rangeCVF.iFirst,g.rangeCVF.iLast);
  939.     PrintHeader(ach);
  940.  
  941.     for (iSec=g.rangeCVF.iFirst; iSec<=g.rangeCVF.iLast; iSec++) {
  942.         printf("\nCVF sector %ld\n",iSec);
  943.  
  944.         offStart = g.mp.cbPerSec*iSec;
  945.         DumpHex(fh,offStart,g.mp.cbPerSec);
  946.     }
  947. }
  948.  
  949.  
  950. /***    DumpDOSBoot - Dump the DOS boot sector
  951.  *
  952.  *      Entry
  953.  *          fh   - file handle of CVF
  954.  *          g.mp - filled in the MDBPB from CVF
  955.  *
  956.  *      Exit
  957.  *          Formatted DOS Boot sector written to stdout
  958.  */
  959. void DumpDOSBoot(FILEHANDLE fh)
  960. {
  961.     PrintHeader("DOS Boot Sector");
  962.     // Don't format the info, just dump hex bytes
  963.     DumpHex(fh,areg[iregDOSBOOT].ibStart,256);
  964. }
  965.  
  966.  
  967. /***    DumpHeap - Dump the Heap
  968.  *
  969.  *      Entry
  970.  *          fh   - file handle of CVF
  971.  *          g.mp - filled in the MDBPB from CVF
  972.  *          g.rangeHeap - range of Sector Heap to dump
  973.  *
  974.  *      Exit
  975.  *         Selected range from sector heap displayed
  976.  */
  977. void DumpHeap(FILEHANDLE fh)
  978. {
  979.     char    ach[cbOUTPUTLINEMAX];
  980.     DWORD   iSec;
  981.     DWORD   offStart;
  982.  
  983.     sprintf(ach,"Sector Heap entries %ld to %ld",
  984.                     g.rangeHeap.iFirst,g.rangeHeap.iLast);
  985.     PrintHeader(ach);
  986.  
  987.     for (iSec=g.rangeHeap.iFirst; iSec<=g.rangeHeap.iLast; iSec++) {
  988.         printf("\nHeap sector %ld\n",iSec);
  989.  
  990.         // MDFAT sector numbers are 1 less than the CVF sector number
  991.         offStart = g.mp.cbPerSec*(iSec+1);
  992.  
  993.         DumpHex(fh,offStart,g.mp.cbPerSec);
  994.     }
  995. }
  996.  
  997.  
  998. /***    DumpDOSFAT - Dump the DOS FAT
  999.  *
  1000.  *      Entry
  1001.  *          fh   - file handle of CVF
  1002.  *          g.mp - filled in the MDBPB from CVF
  1003.  *
  1004.  *      Exit
  1005.  *          Formatted DOS FAT sector written to stdout
  1006.  */
  1007. void DumpDOSFAT(FILEHANDLE fh)
  1008. {
  1009.     PrintHeader("DOS FAT - just a portion of whole table");
  1010.     DumpHex(fh,areg[iregDOSFAT].ibStart,256);
  1011. }
  1012.  
  1013.  
  1014. /***    DumpDOSRootDir - Dump the DOS Root directory
  1015.  *
  1016.  *      Entry
  1017.  *          fh   - file handle of CVF
  1018.  *          g.mp - filled in the MDBPB from CVF
  1019.  *
  1020.  *      Exit
  1021.  *          Formatted DOS root directory written to stdout
  1022.  */
  1023. void DumpDOSRootDir(FILEHANDLE fh)
  1024. {
  1025.     PrintHeader("DOS Root Directory - just a portion of whole table");
  1026.     DumpHex(fh,areg[iregDOSROOTDIR].ibStart,256);
  1027. }
  1028.  
  1029.  
  1030. /***    DumpHex - Dump a portion of a file as Hex bytes
  1031.  *
  1032.  *      Entry
  1033.  *          fh    - File handle of CVF
  1034.  *          iseek - File position
  1035.  *          cb    - Count of bytes to dump
  1036.  *
  1037.  *      Exit-Success
  1038.  *          Formatted information written to stdout
  1039.  *
  1040.  *      Exit-Failure
  1041.  *          Exits program with error message.
  1042.  *          NOTE: Some output may have been written to stdout
  1043.  */
  1044. void DumpHex(FILEHANDLE fh, long iseek, long cb)
  1045. {
  1046.     long    cbPiece;
  1047.     RETCODE rc;
  1048.     int     iLine;
  1049.     int     cLine;
  1050.     DWORD   iseekPiece;
  1051.     BYTE   *pb;
  1052.  
  1053.     iseekPiece = iseek;
  1054.  
  1055.     // Process one piece at a time (limited by our buffer size)
  1056.     while (cb > 0) {
  1057.         cbPiece = (WORD)min(cb,sizeof(g.ab)); // Size of piece to process
  1058.         cLine = (int)(((long)cbPiece)+cbPERLINE-1)/cbPERLINE; // count of lines
  1059.  
  1060.         // Get a piece of the file to print
  1061.         rc = FileRead(fh,iseekPiece,g.ab,sizeof(g.ab));
  1062.         if (rc)
  1063.             Error("Read failed on CVF",NULL);
  1064.  
  1065.         pb = g.ab;                      // Start of buffer
  1066.         // Print the piece one line at a time
  1067.         for (iLine=0; iLine<cLine; iLine++) {
  1068.             DumpHexLine(iseekPiece,pb); // Print a line
  1069.             pb += cbPERLINE;            // Advance buffer pointer
  1070.             iseekPiece += cbPERLINE;    // Advance seek position
  1071.         }
  1072.         cb -= cbPiece;                  // Reduce amount left to dump
  1073.     }
  1074. }
  1075.  
  1076.  
  1077. /***    DumpHexLine - Dump one line of hex output to stdout
  1078.  *
  1079.  *      Entry
  1080.  *          iseek - File position to report
  1081.  *          pb    - Pointer to buffer to dump
  1082.  *
  1083.  *      Exit
  1084.  *
  1085.  *  Sample HEX output
  1086.  *
  1087.  *  123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345
  1088.  *  0000    30 31 32 33 34 35 36 37  38 39 00 00 00 00 00 30   0123456789.....0
  1089.  *
  1090.  */
  1091. void DumpHexLine(long iseek, BYTE *pb)
  1092. {
  1093.     char    ch;
  1094.     int     i;
  1095.     int     ich;
  1096.     char   *pbSave;
  1097.  
  1098. #define cchHEXFILEOFFSET    10
  1099. #define cchHEXVALUE          3
  1100.  
  1101.     pbSave = (char *)pb;                // Save pointer for ASCII section
  1102.  
  1103.     // Generate file offset
  1104.     sprintf(g.ach,"%8lx  ",iseek);
  1105.     ich = cchHEXFILEOFFSET;
  1106.  
  1107.     // Generate hex values
  1108.     for (i=0; i<cbPERLINE; ) {
  1109.         sprintf(&g.ach[ich],"%02x ",pb[i]);
  1110.         ich += cchHEXVALUE;
  1111.         i++;
  1112.         // Check for column break
  1113.         if ( (i % (cbPERLINE/cCOLUMNS)) == 0) {
  1114.             g.ach[ich++] = ' ';             // Add column separator
  1115.         }
  1116.     }
  1117.  
  1118.     g.ach[ich++] = ' ';                 // Add space before ASCII section
  1119.  
  1120.     // Generate ASCII section
  1121.     for (i=0; i<cbPERLINE; i++) {
  1122.         if (isprint(pb[i]))
  1123.             ch = pb[i];
  1124.         else
  1125.             ch = chNOTPRINTABLE;
  1126.         g.ach[ich++] = ch;
  1127.     }
  1128.     g.ach[ich++] = '\0';                // Terminate line
  1129.  
  1130.     printf("%s\n",g.ach);               // Print it
  1131. }
  1132.  
  1133. #endif    // ifndef SNAP    ------------------------------------------------
  1134.  
  1135.  
  1136. /***    FileRead - read data from file at particular location
  1137.  *
  1138.  *      Entry
  1139.  *          fh    - File handle
  1140.  *          iseek - Byte offset from start of file
  1141.  *                      0 = front of file
  1142.  *                     -1 = current file position
  1143.  *          pb    - Buffer to receive data
  1144.  *          cb    - Size of buffer
  1145.  *
  1146.  *      Exit-Success
  1147.  *          Returns 0
  1148.  *          pb filled in with requested data
  1149.  *
  1150.  *      Exit-Failure
  1151.  *          Returns non-zero error code
  1152.  */
  1153. RETCODE FileRead(FILEHANDLE fh, long iseek, void *pb, WORD cb)
  1154. {
  1155.     WORD    cbRead;
  1156.     long    iseekNew;
  1157.     WORD    rc;
  1158.  
  1159.     // Seek to requested location
  1160.     if (iseek != FR_CURRENT_POS) {
  1161.         iseekNew = lseek(fh,iseek,SEEK_SET);
  1162.         if (iseekNew != iseek)
  1163.             return errno;
  1164.     }
  1165.  
  1166.     // Read data
  1167.     rc = _dos_read(fh,pb,cb,&cbRead);
  1168.     if (rc != 0)                        // Call failed
  1169.         return rc;
  1170.     if (cbRead != cb)                   // Did not get enough data
  1171.         return 1;
  1172.  
  1173.     return 0;                           // Success
  1174. }
  1175.  
  1176.  
  1177. /***    Error - format error message, display it, and exit
  1178.  *
  1179.  *      Entry
  1180.  *          pszMsg  - error message
  1181.  *          pszParm - replacable parm for %s in pszMsg
  1182.  *
  1183.  *      Exit
  1184.  *          message formatted and displayed
  1185.  *          exit program
  1186.  */
  1187. void Error(char *pszMsg, char *pszParm)
  1188. {
  1189.     printf("Error: ");
  1190.     printf(pszMsg,pszParm);
  1191.     exit(1);
  1192. }
  1193.  
  1194. #ifndef    SNAP    // ------------------------------------------------------
  1195.  
  1196. /***    FormatMember - Format a value, as indicated by its MEMBERTYPE
  1197.  *
  1198.  *      Entry
  1199.  *          pch - Buffer to fill in
  1200.  *          pv  - Pointer to value
  1201.  *          mt  - MEMBERTYPE of value
  1202.  *
  1203.  *      Exit
  1204.  *          pch filled in with formatted value
  1205.  */
  1206. void FormatMember(char *pch,void *pv, MEMBERTYPE mt)
  1207. {
  1208.     int     i;
  1209.     WORD    w;
  1210.     WORD    w2;
  1211.     WORD    w3;
  1212.  
  1213.     mt = mt & (~mtHIDE);                // Mask off hidden attribute
  1214.  
  1215.     switch (mt) {
  1216.  
  1217.     case mtBYTE  :
  1218.         w = (WORD)(*(BYTE *)pv);        // WORD from BYTE
  1219.         sprintf(pch,"%02x",w);
  1220.         break;
  1221.  
  1222.     case mtBYTE3 :
  1223.         w  = (WORD)(*((BYTE *)pv+0));        // WORD from BYTE
  1224.         w2 = (WORD)(*((BYTE *)pv+1));        // WORD from BYTE
  1225.         w3 = (WORD)(*((BYTE *)pv+2));        // WORD from BYTE
  1226.         sprintf(pch,"%02x %02x %02x", w, w2, w3);
  1227.         break;
  1228.  
  1229.     case mtCHAR  :
  1230.         sprintf(pch,"%c",*(char *)pv);
  1231.         break;
  1232.  
  1233.     case mtCHAR8 :
  1234.         memcpy(g.ach,(char *)pv,8);     // Copy string
  1235.         g.ach[8] = '\0';                // Add null terminator
  1236.         sprintf(pch,"%s",g.ach);
  1237.         break;
  1238.  
  1239.     case mtDWORD :
  1240.         sprintf(pch,"%08lx",*(DWORD *)pv);
  1241.         break;
  1242.  
  1243.     case mtINT1  :
  1244.         i = (int)(*(char *)pv);         // 2 byte int from 1 byte int
  1245.         sprintf(pch,"%3d",i);
  1246.         break;
  1247.  
  1248.     case mtINT2  :
  1249.         sprintf(pch,"%5d",*(short *)pv);
  1250.         break;
  1251.  
  1252.     case mtINT4  :
  1253.         sprintf(pch,"%10ld",*(long *)pv);
  1254.         break;
  1255.  
  1256.     case mtUINT1 :
  1257.         w = (WORD)(*(BYTE *)pv);        // WORD from BYTE
  1258.         sprintf(pch,"%3u",w);
  1259.         break;
  1260.  
  1261.     case mtUINT2 :
  1262.         sprintf(pch,"%5u",*(WORD *)pv);
  1263.         break;
  1264.  
  1265.     case mtUINT4 :
  1266.         sprintf(pch,"%10lu",*(DWORD *)pv);
  1267.         break;
  1268.  
  1269.     case mtWORD  :
  1270.         sprintf(pch,"%04x",*(WORD *)pv);
  1271.         break;
  1272.  
  1273.     default :
  1274.         Error("Unexpected MEMBERTYPE",NULL);
  1275.  
  1276.     }
  1277. }
  1278.  
  1279.  
  1280. /***    PrintHeader - print header for a report section
  1281.  *
  1282.  *      Entry
  1283.  *          psz - section name
  1284.  *
  1285.  *      Exit
  1286.  *          Section name written to stdout with underlines.
  1287.  */
  1288. void PrintHeader(char *psz)
  1289. {
  1290.     PrintHeader2(psz,NULL);
  1291. }
  1292.  
  1293.  
  1294. /***    PrintHeader2  - print two line header for a report section
  1295.  *
  1296.  *      Entry
  1297.  *          psz1 - first line
  1298.  *          psz2 - second line (ignored if NULL)
  1299.  *
  1300.  *      Exit
  1301.  *          Lines written to stdout, with underlines after second line.
  1302.  */
  1303. void PrintHeader2(char *psz1, char *psz2)
  1304. {
  1305.     int     cb;
  1306.     int     cb2;
  1307.  
  1308.     printf("\n%s\n",psz1);                // Print name
  1309.  
  1310.     cb = strlen(psz1);
  1311.  
  1312.     // Print 2nd line, and update longest line length
  1313.     if (psz2) {
  1314.         printf("%s\n",psz2);
  1315.         cb2 = strlen(psz2);
  1316.         if (cb2 > cb)
  1317.             cb = cb2;
  1318.     }
  1319.  
  1320.     // Generate and print underline
  1321.     memset(g.ach,'-',cb);
  1322.     g.ach[cb] = '\0';
  1323.     printf("%s\n",g.ach);
  1324. }
  1325.  
  1326.  
  1327. #endif    // ifndef SNAP ------------------------------------------------
  1328.  
  1329. /***    ParseArgs - Parse command-line arguments
  1330.  *
  1331.  *      Entry
  1332.  *          argc - count of arguments
  1333.  *          argv - array of arguments
  1334.  *
  1335.  *      Exit-Success
  1336.  *          GLOBAL structure (g) fields filled in
  1337.  *
  1338.  *      Exit-Failure
  1339.  *          Prints message and exits program.
  1340.  */
  1341. void ParseArgs(int argc, char **argv)
  1342. {
  1343.     char    ch;
  1344.     BOOL    fCVFSeen=0;     // TRUE if CVF name or drive seen
  1345.     BOOL    fFlagSeen=0;    // TRUE if any flags specified
  1346.     BOOL    fFlagSeenOld=0; // Value of fFlagSeen on previous loop iteration
  1347.     int     i;
  1348.     char   *pch;
  1349.  
  1350.     // Save command line pointers for later
  1351.     g.argc = argc;
  1352.     g.argv = argv;
  1353.  
  1354. #ifndef SNAP
  1355.     // Mark all display ranges as defaults, as no values specified, yet
  1356.  
  1357.     g.rangeBitFAT.iFirst = dwRANGE_DEFAULT;
  1358.     g.rangeBitFAT.iLast  = dwRANGE_DEFAULT;
  1359.  
  1360.     g.rangeCVF.iFirst = dwRANGE_DEFAULT;
  1361.     g.rangeCVF.iLast  = dwRANGE_DEFAULT;
  1362.  
  1363.     g.rangeHeap.iFirst = dwRANGE_DEFAULT;
  1364.     g.rangeHeap.iLast  = dwRANGE_DEFAULT;
  1365.  
  1366.     g.rangeMDFAT.iFirst = dwRANGE_DEFAULT;
  1367.     g.rangeMDFAT.iLast  = dwRANGE_DEFAULT;
  1368. #endif
  1369.  
  1370.     // Parse each argument
  1371.     for (i=1; i<argc; i++) {
  1372.         if ( (argv[i][0] == chSWITCH) ||
  1373.              (argv[i][0] == chSWITCH2)  ) {
  1374.  
  1375.             fFlagSeenOld = fFlagSeen;
  1376.             fFlagSeen = 1;
  1377.             pch = &argv[i][1];          // Start with first character
  1378.             while (*pch) {
  1379.                 ch = (char)toupper((int)*pch);
  1380.  
  1381.                 switch (ch) {
  1382.                 case 'I':
  1383.                     g.fIgnoreSigCheck = 1;
  1384.                     fFlagSeen = fFlagSeenOld; // No display, so ignore as flag
  1385.                     break;
  1386. #ifdef    SNAP
  1387.                 case 'O':   // output file specified
  1388.                     pch++;
  1389.                     if ((*pch == '\0') || (*pch != '=') || (pch[1] == '\0') )
  1390.                         Error("Bad parameter format: %s",argv[i]);
  1391.                     else {  // Use specified file
  1392.                         strcpy(g.achOutFileName,pch+1);
  1393.                         pch[1] = '\0';  // Stop parsing this token
  1394.                         // NOTE: We set pch[1], because loop does pch++
  1395.                         //       *before* checking for null terminator!
  1396.                     }
  1397.                     break;
  1398.  
  1399. #else  // SNAP
  1400.                 case 'A':   g.fShowAddresses     = 1; break;
  1401.                 case 'F':   g.fShowDOSFAT        = 1; break;
  1402.                 case 'G':   g.fShowFragmentation = 1; break;
  1403.                 case 'H':   g.fShowHeader        = 1; break;
  1404.                 case 'R':   g.fShowDOSRootDir    = 1; break;
  1405.                 case 'T':   g.fShowDOSBoot       = 1; break;
  1406.  
  1407.                 case 'B':
  1408.                     g.fShowBitFAT = 1;
  1409.                     ParseRange(&g.rangeBitFAT,&pch);
  1410.                     pch--;              // Point to last char, for pch++ below
  1411.                     break;
  1412.  
  1413.                 case 'C':
  1414.                     g.fShowCVFSectors = 1;
  1415.                     ParseRange(&g.rangeCVF,&pch);
  1416.                     pch--;              // Point to last char, for pch++ below
  1417.                     break;
  1418.  
  1419.                 case 'M':
  1420.                     g.fShowMDFAT = 1;
  1421.                     ParseRange(&g.rangeMDFAT,&pch);
  1422.                     pch--;              // Point to last char, for pch++ below
  1423.                     break;
  1424.  
  1425.                 case 'S':
  1426.                     g.fShowHeap = 1;
  1427.                     ParseRange(&g.rangeHeap,&pch);
  1428.                     pch--;              // Point to last char, for pch++ below
  1429.                     break;
  1430.  
  1431.                 case 'V':
  1432.                     g.fShowVerbose = 1; // Remember verbose
  1433.  
  1434.                     // Set all the other flags, except the sector heap
  1435.                     // and CVF flags, since the output for those are
  1436.                     // very, very large.
  1437.                     g.fShowBitFAT        = 1;
  1438.                     g.fShowDOSBoot       = 1;
  1439.                     g.fShowDOSFAT        = 1;
  1440.                     g.fShowDOSRootDir    = 1;
  1441.                     g.fShowFragmentation = 1;
  1442.                     g.fShowHeader        = 1;
  1443.                     g.fShowMDFAT         = 1;
  1444.             break;
  1445. #endif // SNAP
  1446.                 case '?':
  1447.                     ShowSyntax(1);
  1448.                     break;
  1449.  
  1450.                 default:
  1451.                     g.ach[0] = *pch;
  1452.                     g.ach[1] = '\0';
  1453.                     Error("Unknown switch: %s",g.ach);
  1454.                 }
  1455.                 pch++;
  1456.             }
  1457.         }
  1458.         else if (fCVFSeen) {
  1459.             Error("Too many parameters: %s",argv[i]);
  1460.         }
  1461.         else {  // Must be the drive letter or CVF name
  1462.             if (!BuildCVFName(argv[i],g.achCVFName,
  1463.                                             sizeof(g.achCVFName),&g.chDrive))
  1464.                 Error("%s",g.achCVFName);
  1465.             fCVFSeen = 1;
  1466.         }
  1467.     }
  1468.  
  1469.     if (!fCVFSeen)  // Use default CVF
  1470.         if (!BuildCVFName(NULL,g.achCVFName,sizeof(g.achCVFName),&g.chDrive))
  1471.             Error("%s",g.achCVFName);
  1472.  
  1473. #ifndef SNAP
  1474.     // If no flags specifed, show region addresses
  1475.     if (!fFlagSeen)
  1476.         g.fShowAddresses = 1;
  1477. #endif
  1478. }
  1479.  
  1480.  
  1481. /***    ParseRange - parse range specification from command line
  1482.  *
  1483.  *      Entry
  1484.  *          prange - pointer to RANGE to be filled in
  1485.  *          ppch   - pointer to pointer to switch character immediately
  1486.  *                      preceding text to be parsed.
  1487.  *
  1488.  *      Exit-Success
  1489.  *          Returns filled in prange.  Note that if only a first value
  1490.  *          is specfied (e.g., 2, as opposed to 2-7), then last is set
  1491.  *          equal to first.
  1492.  *          *ppch is updated to point to next character after range.
  1493.  *
  1494.  *      Exit-Failure
  1495.  *          Prints error message and exits
  1496.  */
  1497. void ParseRange(PRANGE prange, char **ppch)
  1498. {
  1499.     BOOL    fStartEnd;      // TRUE => start-end, FALSE => start+count
  1500.     char   *pchOriginal;
  1501.     char   *pch;
  1502.  
  1503.     pchOriginal = *ppch;
  1504.     pch = pchOriginal+1;                // Skip flag character
  1505.  
  1506.     prange->iFirst = ParseDecimal(&pch);
  1507.     if (prange->iFirst == dwRANGE_DEFAULT) { // No number present
  1508.         prange->iLast  = dwRANGE_DEFAULT;    // Last is default, too
  1509.         *ppch = pch;                    // Update parsing pointer
  1510.         return;
  1511.     }
  1512.  
  1513.     // Got First number, see if Last number or Count follows
  1514.  
  1515.     switch (*pch) {
  1516.         case chRANGE_START_END:
  1517.             fStartEnd = TRUE;
  1518.             break;
  1519.  
  1520.         case chRANGE_START_COUNT:
  1521.             fStartEnd = FALSE;
  1522.             break;
  1523.  
  1524.         default:    // No second number, so set last == first
  1525.             prange->iLast = prange->iFirst;
  1526.             *ppch = pch;                    // Update parsing pointer
  1527.             return;
  1528.     }
  1529.  
  1530.     // Get Last number
  1531.  
  1532.     pch++;                              // Skip separator
  1533.     prange->iLast = ParseDecimal(&pch);
  1534.     if (prange->iLast == dwRANGE_DEFAULT) { // No number present
  1535.         pchOriginal[1] = '\0';  // Trim off all but switch character
  1536.         Error("Bad range specified for switch %s",pchOriginal);
  1537.     }
  1538.  
  1539.     if (fStartEnd) {                    // Make sure last >= first
  1540.         if (prange->iFirst > prange->iLast) {
  1541.             pchOriginal[1] = '\0';  // Trim off all but switch character
  1542.             Error("First > Last in range for switch %s",pchOriginal);
  1543.         }
  1544.     }
  1545.     else {                              // Set last to first+count-1
  1546.         prange->iLast += (prange->iFirst - 1);
  1547.     }
  1548.  
  1549.     // Update parsing pointer
  1550.     *ppch = pch;
  1551. }
  1552.  
  1553.  
  1554. /***    ParseDecimal - Parse decimal number
  1555.  *
  1556.  *      Entry
  1557.  *          ppch   - pointer to pointer to potential number
  1558.  *
  1559.  *      Exit-Success
  1560.  *          Returns a DWORD.
  1561.  *          *ppch adjusted to point immediately following parsed number.
  1562.  *
  1563.  *      Exit-Failure
  1564.  *          Returns dwRANGE_DEFAULT -- no number was present
  1565.  */
  1566. DWORD ParseDecimal(char **ppch)
  1567. {
  1568.     DWORD   dw=0;
  1569.     char   *pch=*ppch;
  1570.  
  1571.     if (!isdigit(*pch)) {               // No number here
  1572.         return dwRANGE_DEFAULT;
  1573.     }
  1574.  
  1575.     while (isdigit(*pch)) {
  1576.         dw = dw*10 + (*pch - '0');
  1577.         pch++;
  1578.     }
  1579.  
  1580.     *ppch = pch;                        // Update command line pointer
  1581.     return dw;                          // Return parsed number
  1582. }
  1583.  
  1584.  
  1585. /***    ShowSyntax - Display command-line syntax
  1586.  *
  1587.  */
  1588. void ShowSyntax(BOOL fFull)
  1589. {
  1590.     int     i;
  1591.     int     cLines;
  1592.  
  1593.     if (fFull) {                        // Show full help
  1594.         cLines = sizeof(apszSyntax)/sizeof(char *);
  1595.         for (i=0; i<cLines; i++) {
  1596.             printf("%s\n",apszSyntax[i]);
  1597.         }
  1598.     }
  1599.     else {                              // Just show summary line
  1600.         printf("%s\n",apszSyntax[isynSUMMARY]);
  1601.     }
  1602.  
  1603.     exit(0);
  1604. }
  1605.  
  1606.  
  1607. /***    PrintBanner - print banner for this program
  1608.  *
  1609.  */
  1610. void PrintBanner(void)
  1611. {
  1612. #ifndef    SNAP
  1613.     printf("%s File Dumper - Version %d.%02d\n",szProduct,verMAJOR,verMINOR);
  1614. #else
  1615.     printf("%s Snapper - Version %d.%02d\n",szProduct,verMAJOR,verMINOR);
  1616. #endif
  1617. }
  1618.  
  1619. #ifdef    SNAP    // -----------------------------------------------------
  1620.  
  1621. /***    SnapCVF - 'snap' CVF reserved info to file
  1622.  *
  1623.  *    Entry
  1624.  *        fhCVF - file handle of open CVF file
  1625.  */
  1626.  
  1627. void SnapCVF(FILEHANDLE fhCVF)
  1628. {
  1629.     FILEHANDLE  fhOut;
  1630.     int         iErr = 0;
  1631.     UINT        iThisLen;
  1632.     UINT        iOutLen;
  1633.     UINT        iLen = 60 * 1024;
  1634.     long        lCpyLen;
  1635.     char       *pCpyBuf;
  1636.  
  1637.     // Open/create output file
  1638.  
  1639.     if (_dos_creat(g.achOutFileName, _A_NORMAL, &fhOut) != 0)
  1640.         Error("Could not create: %s", g.achOutFileName);
  1641.  
  1642.     // Allocate copy buffer
  1643.  
  1644.     while ( ((pCpyBuf = malloc(iLen)) == NULL) && (iLen > 4 * 1024) )
  1645.     iLen -= 4 * 1024;
  1646.  
  1647.     if (pCpyBuf == NULL)
  1648.     Error("Insufficient memory for copy buffer",NULL);
  1649.  
  1650.     // Tell user what we are doing
  1651.     if (g.chDrive != 0)
  1652.         sprintf(g.ach,"Drive %c: (mounted from %s)",g.chDrive,g.achCVFName);
  1653.     else
  1654.         strcpy(g.ach,g.achCVFName);
  1655.     printf("Writing snapshot of %s to %s\n",g.ach,g.achOutFileName);
  1656.  
  1657.     // Copy CVF up to and including root directory to out file
  1658.  
  1659.     lCpyLen = areg[iregSECTORHEAP].ibStart;    // copy to start of sector heap
  1660.     lseek(fhCVF, 0L, SEEK_SET);         // start at the begining
  1661.  
  1662.     while (lCpyLen > 0) {
  1663.     iThisLen = (lCpyLen > (long)iLen) ? iLen : (int)lCpyLen;
  1664.     if (FileRead(fhCVF,FR_CURRENT_POS,pCpyBuf,iThisLen) != 0)
  1665.     {
  1666.         iErr = 1;
  1667.         break;
  1668.     }
  1669.  
  1670.     if (_dos_write(fhOut, pCpyBuf, iThisLen, &iOutLen) != 0 ||
  1671.         iOutLen != iThisLen)
  1672.     {
  1673.         iErr = 2;
  1674.         break;
  1675.     }
  1676.  
  1677.     lCpyLen -= iThisLen;
  1678.     }
  1679.  
  1680.     // Clean up and exit
  1681.  
  1682.     free(pCpyBuf);
  1683.  
  1684.     _dos_close(fhOut);
  1685.  
  1686.     if (iErr)
  1687.     Error((iErr==1) ? "Error while reading CVF" :
  1688.               "Error while writing snap file", NULL);
  1689. }
  1690.  
  1691. #endif    // SNAP --------------------------------------------------------
  1692.  
  1693.  
  1694. /***    ComputeRegions - Compute regions of CVF, from MDBPB
  1695.  *
  1696.  *      NOTE: This is the most interesting part of the program!
  1697.  *
  1698.  *      Entry
  1699.  *          fh   - File handle of CVF
  1700.  *          g.mp - Filled in with MD BPB from CVF already
  1701.  *
  1702.  *      Exit-Success
  1703.  *          areg filled in.
  1704.  *
  1705.  *      Exit-Failure
  1706.  *          Prints error message and exits.
  1707.  */
  1708. void ComputeRegions(FILEHANDLE fh)
  1709. {
  1710.     int     ireg;                       // Index to walk region table
  1711.     int     cbPerSec;                   // Count of bytes per sector
  1712.     char    csecPerClu;                 // Count of sectors per cluster
  1713.     long    ccluTotal;                  // Current total clusters
  1714.     long    ccluTotalMax;               // Maximum total clusters
  1715.     long    csecTotal;                  // Current total sectors
  1716.     long    csecTotalMax;               // Maximum total sectors
  1717.     long    seekpos;                    // File seek position
  1718.  
  1719.     // Get common values, to make code more readable
  1720.  
  1721.     cbPerSec   = g.mp.cbPerSec;         // Count of bytes per sector
  1722.     csecPerClu = g.mp.csecPerClu;       // Count of sectors per cluster
  1723.  
  1724.     // Get drive size reported to DOS when CVF is mounted
  1725.  
  1726.     if (g.mp.csecTotalWORD != 0)        // Small drive
  1727.         csecTotal = g.mp.csecTotalWORD;
  1728.     else                                // Large drive
  1729.         csecTotal = g.mp.csecTotalDWORD;
  1730.     ccluTotal = csecTotal/csecPerClu;   // Total number of clusters
  1731.  
  1732.     // Check CVF signatures
  1733.  
  1734.     seekpos = (g.mp.csecMDReserved + 1) * (long)cbPerSec;
  1735.     CheckSignature(fh, seekpos, "first");
  1736.  
  1737.     if ((g.cbCVF = lseek(fh,0L,SEEK_END)) == -1L)
  1738.         Error("Could not seek to end of CVF",NULL);
  1739.  
  1740.     // The 2nd stamp is located at the start of the last complete sector
  1741.     // in the CVF.  If the CVF is exactly a sector multiple, then this
  1742.     // is indeed the last sector of the file.  However, sometimes CVFs are
  1743.     // not exactly a sector multiple in length, in which case it is the
  1744.     // next to last sector of the CVF which contains the 2nd stamp.
  1745.  
  1746.     g.ibCVFStamp2 = (g.cbCVF/cbPerSec - csecRETRACT_STAMP) * cbPerSec;
  1747.     CheckSignature(fh, g.ibCVFStamp2, "second");
  1748.  
  1749.     // Get Maximum CVF size information
  1750.  
  1751.     csecTotalMax = (g.mp.cmbCVFMax * 1024L * 1024L) / cbPerSec;
  1752.     ccluTotalMax = csecTotalMax / csecPerClu;
  1753.  
  1754.     // Compute MDBPB region
  1755.  
  1756.     ireg = iregMDBPB;
  1757.     areg[ireg].ibStart  = 0L;           // Always first thing in the CVF
  1758.     areg[ireg].cbTotal  = cbPerSec;     // Always consumes one sector
  1759.     areg[ireg].cbActive = sizeof(MDBPB); // Only MDBPB structure is valid
  1760.  
  1761.     // Compute BitFAT region
  1762.  
  1763.     ireg++;                             // iregBITFAT
  1764.     areg[ireg].ibStart    = areg[ireg-1].ibStart + areg[ireg-1].cbTotal;
  1765.  
  1766.     // The BitFAT cbTotal should also ==
  1767.     //     (cmbCVFMax * 1024 * 1024) / (8 * cbPerSec)
  1768.     //     (file capicity in sectors / 8 sector bits per byte)
  1769.  
  1770.     areg[ireg].cbTotal  = g.mp.cpageBitFAT * (long)cbPER_BITFAT_PAGE;
  1771.     // areg[iregBIITFAT].cbActive is computed below, after we know how large
  1772.     // the sector heap is.
  1773.  
  1774.     // Compute RESERVED1 region
  1775.  
  1776.     ireg++;                             // iregRESERVED1
  1777.     areg[ireg].ibStart  = areg[iregBITFAT].ibStart + areg[iregBITFAT].cbTotal;
  1778.     areg[ireg].cbTotal  = csecRESERVED1 * (long)cbPerSec;
  1779.     areg[ireg].cbActive = 0;            // none in use
  1780.  
  1781.     // Compute MDFAT region
  1782.  
  1783.     ireg++;                // iregMDFAT
  1784.  
  1785.     // The MDFAT starts just after the BitFAT so
  1786.     //     secMDFATStart * cbPerSec + cbPerSec (for MDBPB) should ==
  1787.     //     areg[iregBITFAT].ibStart + areg[iregBITFAT].cbTotal
  1788.  
  1789.     areg[ireg].ibStart    = g.mp.secMDFATStart * (long)cbPerSec + (long)cbPerSec;
  1790.  
  1791.     // The MDFAT size depends on the maximum number of clusters that the CVF
  1792.     // could hold, but we will compute it instead by inference from the
  1793.     // other information we have, so we'll calculate the MDFAT size as
  1794.     // MDReserved - BitFAT size - MDBPB size - other reserved sizes
  1795.  
  1796.     areg[ireg].cbTotal    = (g.mp.csecMDReserved * (long)cbPerSec) -
  1797.               areg[iregMDBPB].cbTotal -    // MDBPB size
  1798.               areg[iregBITFAT].cbTotal -    // BitFAT size
  1799.                           areg[iregRESERVED1].cbTotal - // RESERVED1 size
  1800.                           (csecRESERVED2 * (long)cbPerSec); // RESERVED2 size
  1801.     areg[ireg].cbActive = ccluTotal * cbMDFATENTRY;
  1802.  
  1803.     // Compute RESERVED2 region
  1804.  
  1805.     ireg++;                             // iregRESERVED2
  1806.     areg[ireg].ibStart    = areg[iregMDFAT].ibStart + areg[iregMDFAT].cbTotal;
  1807.     areg[ireg].cbTotal  = csecRESERVED2 * (long)cbPerSec;
  1808.     areg[ireg].cbActive = 0;            // none in use
  1809.  
  1810.     // Compute BOOT  region
  1811.  
  1812.     ireg++;                             // iregDOSBOOT
  1813.     areg[ireg].ibStart  = g.mp.csecMDReserved * (long)cbPerSec;
  1814.     areg[ireg].cbTotal    = cbPerSec;
  1815.     areg[ireg].cbActive = cbPerSec;
  1816.  
  1817.     // Compute RESERVED3 region
  1818.  
  1819.     ireg++;                             // iregRESERVED3
  1820.     areg[ireg].ibStart  = areg[iregDOSBOOT].ibStart
  1821.                           + areg[iregDOSBOOT].cbTotal;
  1822.     areg[ireg].cbTotal    = (g.mp.csecReserved - 1) * (long)cbPerSec;
  1823.     areg[ireg].cbActive = 0;
  1824.  
  1825.     // Compute DOSFAT region
  1826.  
  1827.     ireg++;                             // iregDOSFAT
  1828.     areg[ireg].ibStart    = areg[iregDOSBOOT].ibStart +
  1829.               (g.mp.csecReserved * (long)cbPerSec);
  1830.     areg[ireg].cbTotal    = g.mp.csecFAT * (long)cbPerSec;
  1831.     areg[ireg].cbActive = g.mp.f12BitFAT ?
  1832.               ((ccluTotal * 3)/2) :     // 12-bit FAT
  1833.               (ccluTotal * 2);        // 16-bit FAT
  1834.  
  1835.     // Compute ROOTDIR  region
  1836.  
  1837.     ireg++;                             // iregDOSROOTDIR
  1838.     areg[ireg].ibStart    = (g.mp.secRootDirStart + g.mp.csecMDReserved)
  1839.               * (long)cbPerSec;
  1840.     areg[ireg].cbTotal    = g.mp.cRootDirEntries * cbDIR_ENT;
  1841.     areg[ireg].cbActive = areg[ireg].cbTotal;
  1842.  
  1843.     // Compute RESERVED4 region
  1844.  
  1845.     ireg++;                             // iregRESERVED4
  1846.     areg[ireg].ibStart  = areg[iregDOSROOTDIR].ibStart
  1847.                           + areg[iregDOSROOTDIR].cbTotal;
  1848.     areg[ireg].cbTotal  = csecRESERVED4 * (long)cbPerSec;
  1849.     areg[ireg].cbActive = 0;
  1850.  
  1851.     // Compute SECTORHEAP  region
  1852.  
  1853.     ireg++;                             // iregSECTORHEAP
  1854.     areg[ireg].ibStart  = areg[iregRESERVED4].ibStart
  1855.                           + areg[iregRESERVED4].cbTotal;
  1856.  
  1857.     // Total and Active SECTORHEAP sizes are the same -- unlike other
  1858.     // regions, the SECTORHEAP is not preallocated for the max capacity.
  1859.     // The SECTORHEAP is followed by the 2nd MD STAMP, which occupies
  1860.     // the last <2 sectors of the CVF.  Since we already did the
  1861.     // RETRACT_STAMP computation above, all we have to do is subtract
  1862.     // the start of the sector heap from the start of the 2nd stamp.
  1863.  
  1864.     areg[ireg].cbTotal  = g.ibCVFStamp2 - areg[ireg].ibStart;
  1865.     areg[ireg].cbActive = areg[ireg].cbTotal;
  1866.  
  1867.     // Now we can compute the active region of the BitFAT.  There is
  1868.     // one bit in the BitFAT for every sector in the sector heap, and
  1869.     // we round up to the nearest byte.
  1870.     areg[iregBITFAT].cbActive = (areg[iregSECTORHEAP].cbTotal/cbPerSec + 7) / 8;
  1871.  
  1872. #ifndef SNAP
  1873.     // Compute valid ranges
  1874.  
  1875.     g.rangeCVFValid.iFirst = 0;
  1876.     g.rangeCVFValid.iLast = (g.cbCVF+cbPerSec-1)/cbPerSec;
  1877.  
  1878.     // Heap sector numbers are one less than their CVF position
  1879.     g.rangeHeapValid.iFirst = areg[iregSECTORHEAP].ibStart/cbPerSec - 1;
  1880.     g.rangeHeapValid.iLast  = g.rangeHeapValid.iFirst
  1881.                               + areg[iregSECTORHEAP].cbTotal/cbPerSec
  1882.                               - 1;
  1883.  
  1884.     g.rangeMDFATValid.iFirst = 2 + g.mp.cluFirstData;
  1885.     g.rangeMDFATValid.iLast  = g.rangeMDFATValid.iFirst
  1886.                                + areg[iregMDFAT].cbActive/cbMDFATENTRY
  1887.                                - 1;
  1888.  
  1889.     g.rangeBitFATValid.iFirst = g.rangeHeapValid.iFirst;
  1890.     g.rangeBitFATValid.iLast  = g.rangeHeapValid.iLast;
  1891. #endif
  1892. }
  1893.  
  1894.  
  1895. /***    CheckSignature - Check a CVF signature
  1896.  *
  1897.  *      Entry
  1898.  *          fh      - File handle of CVF
  1899.  *          seekpos - Position to check
  1900.  *          pszName - Signature name
  1901.  *
  1902.  *      Exit-Success
  1903.  *          Returns.
  1904.  *
  1905.  *      Exit-Success
  1906.  *          Prints error;
  1907.  *          if g.fIgnoreSigCheck is set, returns
  1908.  *          else Exits program
  1909.  */
  1910. void CheckSignature(FILEHANDLE fh, long seekpos, char *pszName)
  1911. {
  1912.     char    achStamp[cbDS_STAMP];       // Buffer to read stamp
  1913.  
  1914.     if (FileRead(fh,seekpos,achStamp,sizeof(achStamp)) != 0) {
  1915.         sprintf(g.ach,"Could not read %s signature: %s",pszName,g.achCVFName);
  1916.         goto Error;
  1917.     }
  1918.     if ( (strcmp(achStamp,szDS_STAMP1) != 0) &&
  1919.          (strcmp(achStamp,szDS_STAMP2) != 0)  )  {
  1920.         sprintf(g.ach,"Bad %s signature: %s",pszName,g.achCVFName);
  1921.         goto Error;
  1922.     }
  1923.     return;                             // Signature is okay
  1924.  
  1925. Error:                                  // Signature is bad
  1926.     if (g.fIgnoreSigCheck)
  1927.         printf("%s\n",g.ach);
  1928.     else
  1929.         Error("%s",g.ach);
  1930. }
  1931.  
  1932.  
  1933. /***    BuildCVFName - Parse CVF abbreviations into full file name
  1934.  *
  1935.  *      Entry
  1936.  *          psz      - possible CVF abbreviation
  1937.  *                       <empty>      => Full path of CVF for current drive
  1938.  *                       d:           => Full path of CVF for specified drive
  1939.  *                           NOTE: If drive is not a DS drive, assume it is
  1940.  *                                 a host drive, and take 000.
  1941.  *                       [path]file   => Assume it is a CVF file name
  1942.  *
  1943.  *          ach      - buffer to receive full name
  1944.  *          cb       - length of ach (should be >80 bytes, for error message!)
  1945.  *          pchDrive - receives drive letter of DS drive (if specified)
  1946.  *
  1947.  *      Exit-Success
  1948.  *          returns TRUE
  1949.  *          ach filled in with fully-qualified CVF path;
  1950.  *
  1951.  *      Exit-Failure
  1952.  *          returns FALSE
  1953.  *          ach filled in with error message
  1954.  */
  1955. BOOL BuildCVFName(char *psz, char ach[], int cb, char *pchDrive)
  1956. {
  1957.     SEQ     seq;
  1958.     int     dr;
  1959.     BYTE    drHost;
  1960.     BOOL    fSwapped;
  1961.  
  1962.     if ( (psz == NULL) || (*psz == '\0') ) { // <empty>
  1963.         _dos_getdrive(&dr);             // Get current drive number (1-based)
  1964.         dr--;                           // Make drive zero-based
  1965.     }
  1966.     else if (IsDriveSpec(psz)) {        // Could be just drive letter
  1967.         dr = (char)toupper((int)psz[0])-'A'; // Zero-based drive number
  1968.     }
  1969.     else {                              // Allow any file name
  1970.         strcpy(ach,psz);
  1971.         *pchDrive = 0;                  // Drive not specified
  1972.         return TRUE;
  1973.     }
  1974.  
  1975.     // Build CVF path from host drive and sequence number
  1976.  
  1977.     if (IsDoubleSpaceDrive((BYTE)dr,&fSwapped,&drHost,&seq)) {
  1978.         *pchDrive = (char)(dr+'A');     // Mounted drive letter
  1979.         sprintf(ach,"%c:\\%s.%03d",(int)drHost+'A',szCVF_ROOT,seq);
  1980.         return TRUE;
  1981.     }
  1982.     else {
  1983.         sprintf(ach,"Not a DoubleSpace drive: %c",dr+'A');
  1984.         return FALSE;
  1985.     }
  1986. }
  1987.  
  1988.  
  1989. /***    IsDriveSpec - checks if parameter is a valid drive specifier
  1990.  *
  1991.  *      Entry
  1992.  *          psz - string to check; valid forms are:
  1993.  *                  "a" or "a:", where a is in [a..zA..Z]
  1994.  *
  1995.  *      Exit-Success
  1996.  *          Returns TRUE.
  1997.  *
  1998.  *      Exit-Failure
  1999.  *          Returns FALSE.
  2000.  */
  2001. BOOL IsDriveSpec(char *psz)
  2002. {
  2003.     int cb;
  2004.  
  2005.     if (psz == NULL)
  2006.         return FALSE;
  2007.  
  2008.     cb = strlen(psz);
  2009.     if (cb > 2)
  2010.         return FALSE;
  2011.  
  2012.     if (!isalpha(*psz))
  2013.         return FALSE;
  2014.  
  2015.     if ( (cb == 2) && (psz[1] != ':') )
  2016.         return FALSE;
  2017.  
  2018.     return TRUE;
  2019. }
  2020.